﻿using System;
using System.Configuration;
using System.IO;
using System.Xml;

namespace JelleDruyts.BuildCop.Configuration
{
    /// <summary>
    /// Provides static factory methods to create <see cref="BuildCopConfiguration"/> instances.
    /// </summary>
    public static class BuildCopConfigurationFactory
    {
        #region CreateFromAppConfig

        /// <summary>
        /// Creates a <see cref="BuildCopConfiguration"/> instance from the application configuration file.
        /// </summary>
        /// <returns>The <see cref="BuildCopConfiguration"/> from the default application configuration file.</returns>
        public static BuildCopConfiguration CreateFromAppConfig()
        {
            return BuildCopConfiguration.Instance;
        }

        #endregion

        #region CreateFromFile

        /// <summary>
        /// Creates a <see cref="BuildCopConfiguration"/> instance from a string.
        /// </summary>
        /// <param name="configurationFile">The configuration string.</param>
        /// <returns>The <see cref="BuildCopConfiguration"/> for the specified <paramref name="configurationFile"/> file.</returns>
        public static BuildCopConfiguration CreateFromFile(string configurationFile)
        {
            if (string.IsNullOrEmpty(configurationFile))
            {
                throw new ArgumentException("The configuration file is a required argument.", "configurationFile");
            }

            ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
            fileMap.ExeConfigFilename = configurationFile;
            System.Configuration.Configuration configFile = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
            return configFile.GetSection(BuildCopConfiguration.BuildCopConfigurationSectionName) as BuildCopConfiguration;
        }

        #endregion

        #region CreateFromString

        /// <summary>
        /// Creates a <see cref="BuildCopConfiguration"/> instance from a configuration string.
        /// </summary>
        /// <param name="configuration">The configuration string.</param>
        /// <returns>The <see cref="BuildCopConfiguration"/> for the specified <paramref name="configuration"/> string.</returns>
        public static BuildCopConfiguration CreateFromString(string configuration)
        {
            if (string.IsNullOrEmpty(configuration))
            {
                throw new ArgumentException("The configuration string is a required argument.", "configuration");
            }

            // HACK! After some digging I'm still not sure why this is necessary but otherwise
            // an exception is thrown while deserializing a single-line configuration string.
            configuration = configuration.Replace(">", "> ");
            configuration = configuration.Trim();

            using (StringReader stringReader = new StringReader(configuration))
            using (XmlReader reader = XmlReader.Create(stringReader))
            {
                return new BuildCopConfigurationWrapper(reader);
            }
        }

        #endregion

        #region Private BuildCopConfigurationWrapper Class

        // Exposes the DeserializeSection functionality through the constructor.
        private class BuildCopConfigurationWrapper : BuildCopConfiguration
        {
            internal BuildCopConfigurationWrapper(XmlReader reader)
            {
                base.DeserializeSection(reader);
            }
        }

        #endregion
    }
}